// Copyright (c) 2023 Presto Labs Pte. Ltd.
// Author: tianqiang

#pragma once

#include <random>

#include "coin2/exchange/base/api_util/api_signature.h"
#include "coin2/exchange/base/api_util/json_writer.h"
#include "coin2/exchange/mexc/api_util/md5.h"

namespace {
std::string GetRandomString(std::string::size_type length) {
  static auto& chrs =
      "0123456789"
      "abcdefghijklmnopqrstuvwxyz"
      "ABCDEFGHIJKLMNOPQRSTUVWXYZ";

  thread_local static std::mt19937 rg{std::random_device{}()};
  thread_local static std::uniform_int_distribution<std::string::size_type> pick(
      0,
      sizeof(chrs) - 2);

  std::string s;

  s.reserve(length);

  while (length--) {
    s += chrs[pick(rg)];
  }

  return s;
}
}  // namespace

class LbankRestAuth : public BaseRestAuth {
 public:
  explicit LbankRestAuth(
      const std::string& key_filepath,
      const std::string& host,
      const std::string& port,
      const std::string& bind_ip)
      : key_(AuthKey::FromFile(key_filepath)), host_(host), port_(port), bind_ip_(bind_ip) {
    header_.push_back(std::make_pair("Content-Type", "application/x-www-form-urlencoded"));
  }

  explicit LbankRestAuth(
      const std::string& key_filepath,
      const std::string& host,
      int32_t port,
      const std::string& bind_ip)
      : LbankRestAuth(key_filepath, host, std::to_string(port), bind_ip) {}

  AsioHttpContext* CreateRequestByGetWithSignature(
      const std::string& url_path,
      UrlParamsBuilder* builder) override {
    return nullptr;
  }

  AsioHttpContext* CreateRequestByPostWithSignature(
      const std::string& url_path,
      UrlParamsBuilder* builder) override {
    std::string query_string = builder->ComputeQueryString();
    CHECK(query_string.empty());
    std::string timestamp = std::to_string(GetCurrentTimestamp() / 1000000);
    std::string echostr = GetRandomString(35);
    builder->PutPost("api_key", key_.access_key());
    builder->PutPost("timestamp", timestamp);
    builder->PutPost("signature_method", "HmacSHA256");
    builder->PutPost("echostr", echostr);
    std::string post_body = builder->ComputePostBodyAsQueryString();

    MD5 md5 = MD5(post_body);
    std::string prepared_str = md5.hexdigest();
    StringToUpperInPlace(&prepared_str);

    std::string sign =
        ApiSignatureUtil::GetSignatureByHexHmacSHA256(prepared_str, key_.secret_key());
    post_body = ApiSignatureUtil::UrlEncode(post_body + "&sign=" + sign);

    return AsioHttpContext::NewPost(host_, port_, bind_ip_, url_path, header_, post_body);
  }

  AsioHttpContext* CreateRequestByDeleteWithSignature(
      const std::string& url_path,
      UrlParamsBuilder* builder) override {
    return nullptr;
  }

  AsioHttpContext* CreateRequestByPutWithSignature(
      const std::string& address,
      UrlParamsBuilder* builder) override {
    return nullptr;
  }

 protected:
  AuthKey key_;
  std::string host_;
  std::string port_;
  std::string bind_ip_;
  std::vector<std::pair<std::string, std::string>> header_;
};
